package org.rency.mq.activemq.listener.failover.impl;

import org.rency.mq.activemq.jms.service.JmsAccessor;
import org.rency.mq.activemq.listener.failover.FailoverService;
import org.rency.mq.activemq.listener.failover.RetryExecutor;
import org.rency.mq.activemq.listener.failover.RetryStrategy;
import org.rency.mq.activemq.request.MQRequest;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class CommonFailoverServiceImpl implements FailoverService {
	private static final String pkg = "org.rency.common.activemq.listener.failover.impl";
    private JmsAccessor mqAccessor;
    private long interval = 60;
    private long duration = 120;//2880;
    private int frequency = 5;
    private float attenuationFactor = 2f;
    private boolean supportScheduleMessage = false;
    
    private String retryStrategy = "AttenuationRetryStrategy";
    
    private ScheduledExecutorService executorService;	
	private RetryExecutor retryExecutor;
	private RetryStrategy strategy;
	private boolean isSendToFail = false;
	

	public void retry(MQRequest request) {
		if(strategy == null){
			strategy = createRetryStrategy();
			init(strategy);
		}
		
		strategy.retry(request);
	}

	private RetryStrategy createRetryStrategy() {
		String className = null;
		if(retryStrategy.contains(".")){
			className = retryStrategy;
		} else {
			className = pkg + "." + retryStrategy;
		}
		
		try {
			Class<?> clazz = Class.forName(className);
			return (RetryStrategy) clazz.newInstance();
		} catch (Exception e) {
		}
		
		return null;
	}

	private void init(RetryStrategy strategy) {
		AbstractRetryStrategy retryStrategy = (AbstractRetryStrategy)strategy;
		retryStrategy.setInterval(interval);
		retryStrategy.setMqAccessor(mqAccessor);
		retryStrategy.setSendToFail(isSendToFail);
		if(strategy instanceof FrequencyRetryStrategy){
			((FrequencyRetryStrategy)strategy).setFrequency(frequency);
		}else if(strategy instanceof AttenuationRetryStrategy){
			((AttenuationRetryStrategy)strategy).setDuration(duration);
			((AttenuationRetryStrategy)strategy).setAttenuationFactor(attenuationFactor);
		}else if(strategy instanceof DurationRetryStrategy){
			((DurationRetryStrategy)strategy).setDuration(duration);
		}
		
		retryStrategy.setRetryExecutor(getRetryExecutor());
	}

	private RetryExecutor getRetryExecutor() {
		if(retryExecutor != null)return retryExecutor;
		
		if(supportScheduleMessage){
			retryExecutor = new ActiveMQRetryExecutor(mqAccessor);
		} else {
			TimerRetryExecutor timerRetryExecutor = new TimerRetryExecutor(mqAccessor);
			
			timerRetryExecutor.setExecutorService(getExecutorService());
			
			retryExecutor = timerRetryExecutor;
		}
		
		return retryExecutor;
	}

	public void retry(Object request, int retryTimes, String retryQueue,
			String failedQueue) {
		throw new UnsupportedOperationException();
	}

	public void setMqAccessor(JmsAccessor mqAccessor) {
		this.mqAccessor = mqAccessor;
	}
	
	/**
	 * 设置重试的间隔时间（单位秒，默认为30秒钟）
	 * @param interval	
	 */
	public void setInterval(long interval) {
		if(interval <= 0){
			throw new IllegalArgumentException("Interval must be greater than 0.");
		}
		
		this.interval = interval;
	}

	/**
	 * 设置重试持续时间（单位分钟，默认为120，即2小时），重试策略为DURATION或ATTENUATION时设置
	 * @param duration	
	 */
	public void setDuration(long duration) {
		if(duration <= 0){
			throw new IllegalArgumentException("Duration must be greater than 0.");
		}
		
		this.duration = duration;
	}

	/**
	 * 设置重试次数（默认为3），重试策略为FREQUENCY时设置
	 * @param frequency	
	 * @return
	 */
	public void setFrequency(int frequency) {
		if(frequency <= 0){
			throw new IllegalArgumentException("Frequency must be greater than 0.");
		}
		
		this.frequency = frequency;
	}

	/**
	 * 设置重试间隔时间的衰减因子（默认为1.5），重试策略为ATTENUATION时设置
	 * @param attenuationFactor
	 */

	public void setAttenuationFactor(float attenuationFactor) {
		if(attenuationFactor < 1){
			throw new IllegalArgumentException("Attenuation factor must be greater than 1.");
		}
		
		this.attenuationFactor = attenuationFactor;
	}

	/**
	 * 设置重试策略
	 * @param retryStrategy
	 */
	public void setRetryStrategy(String retryStrategy) {
		this.retryStrategy = retryStrategy;
	}

	public boolean isSupportScheduleMessage() {
		return supportScheduleMessage;
	}

	public void setSupportScheduleMessage(boolean supportScheduleMessage) {
		this.supportScheduleMessage = supportScheduleMessage;
	}

	public void setExecutorService(ScheduledExecutorService executorService) {
		this.executorService = executorService;
	}

	public ScheduledExecutorService getExecutorService() {
		if(executorService == null){
			executorService = Executors.newScheduledThreadPool(10);
		}
		
		return executorService;
	}

    public boolean isSendToFail() {
        return isSendToFail;
    }

    public void setSendToFail(boolean isSendFail) {
        this.isSendToFail = isSendFail;
    }

}
